import collections
import logging

import lib

NamespaceElement = collections.namedtuple('NamespaceElement', ('identifier', 'handler'))
NamespaceIdentification = collections.namedtuple('NamespaceIdentification', ('complete', 'tokens', 'possibilities',))
NamespaceContext = collections.namedtuple('NamespaceContext', ('namespace', 'kwargs', 'commit_callback'))
NamespaceResponse = collections.namedtuple('NamespaceResponse', ('message', 'context'))
TreeNode = collections.namedtuple('TreeNode', ('element', 'sufficient', 'children'))
"""
element is the constant-string/variable
sufficient indicates whether stopping after this element forms a complete statement
children is a collection of TreeNodes or None if on a leaf

The root of a tree must always have a null element
"""

_logger = logging.getLogger("lib-ios")

def _validate_token(token, tree):
    if not token:
        return None
        
    if isinstance(tree.element, lib.Variable):
        try:
            return tree.element.parse(token)
        except Exception:
            return None
    elif isinstance(tree.element, lib.VariableList):
        try:
            return tree.element.variable.parse(token)
        except Exception:
            return None
    else:
        if token == tree.element:
            return token
        return None
        
def _get_node_value(node):
    if isinstance(node.element, (lib.Variable, lib.VariableList)):
        return ('({identifier})'.format(
            identifier=node.element.identifier,
        ),)
    return (node.element,)
    
def _complete_node_value(node, token):
    token = token.strip()
    if isinstance(node.element, (lib.Variable, lib.VariableList)):
        if token:
            return ()
        return ('({identifier})'.format(
            identifier=node.element.identifier,
        ),)
    else:
        if token and not node.element.startswith(token):
            return ()
        return (node.element,)
    
def identify_tokens(tokens, tree, processed_tokens=()):
    """
    Maps the given sequence of tokens onto a data-structure describing what is
    recognised as a valid statement.
    """
    if not tokens:
        return NamespaceIdentification(not tree, processed_tokens, _get_node_value(tree))
        
    head = tokens[0]
    tail = tokens[1:]
    token = _validate_token(head, tree)
    if token is not None:
        processed_tokens += (token,)
        if isinstance(tree.element, lib.VariableList):
            try:
                successor = TreeNode(tree.element.step_sequence(), tree.sufficient, None)
                if tree.children:
                    children = list(tree.children) + [successor]
                else:
                    children = [successor]
                tree = TreeNode(tree.element, tree.sufficient, children)
            except Exception:
                return NamespaceIdentification(False, processed_tokens, ())
        complete = not tail and tree.sufficient
        possibilities = []
        complete_tokens = None
        if tree.children:
            for node in tree.children:
                identification = identify_tokens(tail, node, processed_tokens)
                if identification.complete:
                    complete = True
                    complete_tokens = identification.tokens
                possibilities.extend(identification.possibilities)
        return NamespaceIdentification(complete, complete_tokens or processed_tokens, possibilities)
    elif not tail:
        return NamespaceIdentification(False, processed_tokens, _complete_node_value(tree, head))
    return NamespaceIdentification(False, processed_tokens, ())
    